package com.hwg;

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.BufferedReader;

import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class JavaProcessMonitor {
    private static final Map<Long, Long> lastCpuTimes = new HashMap<>();
    private static final Map<Long, Long> lastSampleTimes = new HashMap<>();

    public static void main(String[] args) throws Exception {
        System.out.println("正在查找Java进程...");
        List<VirtualMachineDescriptor> vmds = VirtualMachine.list();

        if (vmds.isEmpty()) {
            System.out.println("未发现任何Java进程。");
            return;
        }

        System.out.println("\n可用的Java进程:");
        System.out.println("PID\t进程名称");
        System.out.println("----------------------------------------");

        for (VirtualMachineDescriptor vmd : vmds) {
            System.out.printf("%s\t%s\n", vmd.id(), vmd.displayName());
        }

        System.out.print("\n请输入要监控的Java进程PID: ");
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String pid = reader.readLine();

        try {
            monitorJavaProcess(pid);
        } catch (Exception e) {
            System.out.println("监控进程时出错: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private static void monitorJavaProcess(String pid) throws Exception {
        // 连接到目标JVM
        VirtualMachine vm = null;
        try {
            vm = VirtualMachine.attach(pid);
            System.out.println("已成功连接到进程 " + pid);

            // 从该进程获取JMX连接
            String connectorAddr = vm.startLocalManagementAgent();

            if (connectorAddr == null) {
                // 如果JMX未启用,尝试加载management-agent.jar
                String javaHome = System.getProperty("java.home");
                String agent = javaHome + "/lib/management-agent.jar";
                vm.loadAgent(agent);
                connectorAddr = vm.startLocalManagementAgent();
            }

            JMXServiceURL serviceURL = new JMXServiceURL(connectorAddr);
            JMXConnector connector = JMXConnectorFactory.connect(serviceURL);
            MBeanServerConnection mbsc = connector.getMBeanServerConnection();

            // 获取ThreadMXBean
            ObjectName threadMXBeanName = new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
            ThreadMXBean threadMXBeanProxy =
                    ManagementFactory.newPlatformMXBeanProxy(mbsc, threadMXBeanName.toString(), ThreadMXBean.class);

            // 检查是否支持线程CPU时间测量
            if (!threadMXBeanProxy.isThreadCpuTimeSupported()) {
                System.out.println("该JVM不支持线程CPU时间测量");
                return;
            }
            threadMXBeanProxy.setThreadCpuTimeEnabled(true);

            // 开始监控
            System.out.println("\n开始监控进程 " + pid + " 中的线程...");
            System.out.println("输入 'q' 退出监控");

            // 创建一个后台线程读取用户输入线程
            Thread inputThread = new Thread(() -> {
                try {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                    while (true) {
                        if (reader.ready() && reader.readLine().equalsIgnoreCase("q")) {
                            System.exit(0);
                        }
                        Thread.sleep(200);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            inputThread.setDaemon(true);
            inputThread.start();

            // 主循环
            while (true) {
                monitorThreads(threadMXBeanProxy);
                TimeUnit.SECONDS.sleep(3);
            }

        } finally {
            if (vm != null) {
                vm.detach();
            }
        }
    }

    private static void monitorThreads(ThreadMXBean threadMXBean) {
        System.out.println("\n线程CPU使用率与堆栈监控 - " + System.currentTimeMillis());
        System.out.println("------------------------------------------------------------------");
        System.out.println("线程ID\t线程名称\t\t\t\tCPU使用率\t状态");
        System.out.println("------------------------------------------------------------------");

        long[] threadIds = threadMXBean.getAllThreadIds();
        ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadIds, true, true);

        for (ThreadInfo info : threadInfos) {
            if (info == null) continue;

            long threadId = info.getThreadId();
            long cpuTime = threadMXBean.getThreadCpuTime(threadId); // 纳秒
            if (cpuTime == -1) continue;

            long currentTime = System.nanoTime();
            double cpuUsage = 0;

            // 计算CPU使用率
            if (lastCpuTimes.containsKey(threadId) && lastSampleTimes.containsKey(threadId)) {
                long lastCpuTime = lastCpuTimes.get(threadId);
                long lastSampleTime = lastSampleTimes.get(threadId);

                long cpuTimeDelta = cpuTime - lastCpuTime;
                long sampleTimeDelta = currentTime - lastSampleTime;

                if (sampleTimeDelta > 0) {
                    cpuUsage = (double) cpuTimeDelta / sampleTimeDelta * 100;
                }
            }

            // 保存上次的CPU时间和采样时间
            lastCpuTimes.put(threadId, cpuTime);
            lastSampleTimes.put(threadId, currentTime);

            // 仅显示有CPU活动线程
            if (cpuUsage > 0.1) {
                String threadName = info.getThreadName();
                Thread.State state = info.getThreadState();

                // 格式化输出
                System.out.printf("%-8d\t%-30s\t%.2f%%\t%s\n",
                        threadId, threadName, cpuUsage, state);

                // 打印线程栈信息
                StackTraceElement[] stackTrace = info.getStackTrace();
                if (stackTrace != null && stackTrace.length > 0) {
                    System.out.println("线程栈:");
                    for (int i = 0; i < stackTrace.length; i++) {
                        System.out.println("\t" + stackTrace[i]);
                        // 限制栈深度，避免输出过多信息
                        if (i >= 10) {
                            System.out.println("\t... 更多栈帧省略 ...");
                            break;
                        }
                    }
                    System.out.println();
                }
            }
        }
    }
}